/*
This is distributed under the the GNU General Public License

This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.
*/

#include "irController.h"
#include "PCInterrupt.h"
#include "IRMapping.h"

volatile int idx;
volatile unsigned long pulseEnd[BUFFERSIZE];
unsigned char bits[BUFFERSIZE];
extern void inline pulseFalling();

PCInterrupt myInt;

IRController::IRController() {}

//! \param devAddress - pin of the device.  Must be an Interrupt pin
//! \param devType = OTHER_DEVICE
//! \return ErrorCode - see Error Codes for descriptions

//! Interrupt driven IRcontroller with simple Remote decoder
int16_t IRController::begin(int pin, DeviceType deviceType)
{
    ErrorCode res;

    DBGLN("** IRController v1.3");

    res = (ErrorCode) Device::begin(pin, deviceType);
    if (res != SUCCESS)return(errorCode = res);

    idx=0;

    // Attach interrrupt handler to this pin
    if ( myInt.attachInterrupt(pin, FALLING, &pulseFalling) ){
        DBGLN("     Interrupt attached");
        return(errorCode = SUCCESS);
    }
    else
    {
        DBGLN(" ERROR - Not INTERRUPT Pin");
        return(errorCode = ERROR_NOT_INTERRUPT_PIN);
    }
}

/*!
Wait for a keypress from the IR remote, and return the integer mapping of that key\n
Returns ERROR_NO_DATA if nothing available.  Returns raw command number if mapping is not recognized.
 */
int16_t IRController::read()
{
    int i, count, key, numBits;

    errorCode = SUCCESS;

    //if (idx > 1 ){
    //DBG("Bits rcvd=");
    //DBGLN(idx);
    //}

    noInterrupts();  //  CRITICAL SECTION >>>>>>>>>>
    numBits = idx;

    // Wait for Interrupt routine to collect pulses
    if (numBits < IR_NUM_BITS){
        interrupts();  //  NON CRITICAL SECTION <<<<<<<<<
        return(errorCode=ERROR_NO_DATA);  // No data available
    }

    if (numBits > IR_NUM_BITS+1){
        numBits = IR_NUM_BITS+1;
    }

    // Calc length of pulses and store in pulseMsec[]
    for (i=0;i<=numBits;i++)
    {
        {
           // if (pulseEnd[i+1] - pulseEnd[i] < START_PULSE_MSEC)  // Ignore pulse if too long
                pulseMsec[i] = pulseEnd[i+1] - pulseEnd[i];
        }
    }

    idx=0;  // Set ISR buffer to get next command

    interrupts();  //  NON CRITICAL SECTION <<<<<<<<<

    pulse_to_bits(bits, numBits);     // Convert the pulses to bits - long pulse=1, short=0
    count = verifyRemote(bits);       // Make sure it was a valid msg
    key =  bits_to_int(bits, count);  // Return the key pressed
    DBG("Raw Val=")
    DBGLN(key);
    return(decode(key) );
}


/*
  IR pulses encode binary "0" as a short pulse, and binary "1"
 as a long pulse.  Given an array containing pulse lengths,
 convert this to an array containing binary values
 */

void IRController::pulse_to_bits(unsigned char bits[], int numBits)
{
    for(int i = 0; i < numBits; i++)
    {      
        if(pulseMsec[i] > BIT_1_MSEC  && pulseMsec[i] < BIT_1_MSEC_MAX) //is it a 1?
        {
            bits[i] = 1;
        }
        else if(pulseMsec[i] > BIT_0_MSEC && pulseMsec[i] < BIT_1_MSEC_MAX) //is it a 0?
        {
            bits[i] = 0;
        }
        // else data is invalid...
        else {
            bits[i] = 2;
            if (pulseMsec[i] > BIT_1_MSEC_MAX && pulseMsec[i]< START_PULSE_MSEC) DBGLN("Not NEC Protocol?]");
        errorCode = ERROR_CHECKSUM;
        }
    }
}

/*
 Remote sends an ID code in the first 14 check bits - we currently don't verify it
 */

int IRController::verifyRemote(unsigned char bits[])
{
    int result = 0;
    int bitValue = 1;
    int zeroBitCount =0;

    //Convert bits to integer
    for(int i = 0 ; i < (FirstLastBit) ; i++)
    {
        if(bits[i] == 1)
        {
            result += bitValue;

            // Find location of first 1 bit
            if(zeroBitCount==0) {
                zeroBitCount=i;
            }
        }

        bitValue *= 2;  // Binary value doubles with each bit
    }

    // Currently doesnt verify ID, just skip it
    return(zeroBitCount);
}


/*
  convert an array of binary values to a single base-10 integer
 */

int IRController::bits_to_int(unsigned char bits[], int zeroBitCnt)
{
    int result = 0;
    int bitValue = 1;

    //Convert bits to integer
    for(int i = (IR_NUM_BITS-FirstLastBit+zeroBitCnt-8) ; i < IR_NUM_BITS+zeroBitCnt-8 ; i++)
    {
        if(bits[i] == 1)
            result += bitValue;

        bitValue *= 2;
    }

    return result;
}

// Interrupt Handler is called whenever there is a  state change on the pin
void inline pulseFalling()
{
    // Every transition,  record time so we can measure pulse length
    pulseEnd[idx++] =  micros();

    // Ignore Start pulses
    if (idx == 2 && (pulseEnd[idx-1] - pulseEnd[idx-2] > START_PULSE_MSEC) )
    {
        pulseEnd[idx-2] = pulseEnd[idx-1];
        idx--;  // Received a start pulse (first pulse longer than MSG_START, ignore
    }

    // Make sure we dont overrun buffer
    if (idx>BUFFERSIZE) idx = 0;
}


//! Decode specific remote-control keys - change IRMapping.h with values for your Remote
int IRController::decode(int key)
{
    return key;
/*
    switch (key)
    {
    case RAW_POWER_BUTTON:  // turns on/off power
        return(POWER_BUTTON);
        break;
    case RAW_STOP_BUTTON:  // FUNC/STOP
        return(STOP_BUTTON);
        break;
    case RAW_BACK_BUTTON:  // |<<
        return(BACK_BUTTON);
        break;
    case RAW_PLAY_BUTTON:  // >||
        return(PLAY_BUTTON);
        break;
    case RAW_FORWARD_BUTTON:  // >>|
        return(FORWARD_BUTTON);
        break;
    case RAW_VLMUP_BUTTON:  // VOL+
        return(VLMUP_BUTTON);
        break;
    case RAW_VLMDOWN_BUTTON:  // VOL-
        return(VLMDOWN_BUTTON);
        break;
    case RAW_DOWN_BUTTON:  // v scroll down
        return(DOWN_BUTTON);
        break;
    case RAW_UP_BUTTON:  // ^ scroll up
        return(UP_BUTTON);
        break;
    case RAW_EQ_BUTTON:  // EQ
        return(EQ_BUTTON);
        break;
    case RAW_REP_BUTTON:  // ST/REPT
        return(REP_BUTTON);
        break;
    case RAW_0:  // 0
        return(0);
        break;
    case RAW_1:  // 1
        return(1);
        break;
    case RAW_2:  // 2
        return(2);
        break;
    case RAW_3:  // 3
        return(3);
        break;
    case RAW_4:  // 4
        return(4);
        break;
    case RAW_5:  // 5
        return(5);
        break;
    case RAW_6:  // 6
        return(6);
        break;
    case RAW_7:  // 7
        return(7);
        break;
    case RAW_8:  // 8
        return(8);
        break;
    case RAW_9:  // 9
        return(9);
        break;
    default:
        errorCode = ERROR_UNKNOWN_RSP;
        DBGLN("Unable to map.  Update IRMapping.h with the raw val above.");
        return(key);  // Set errorCode code but return unrecognized raw key
        break;
    }
*/
}
